

#include "cybertron/core/FiniteStateMachine.hpp"

CYBERTRON_BEGIN

CybertronCoreAPI FiniteStateMachine::FiniteStateMachine()
{
	zeroMembers();
}

CybertronCoreAPI FiniteStateMachine::FiniteStateMachine(const FiniteStateMachine& rhs)
{
	mCurrentState = rhs.mCurrentState;
	mEnterStateFunctions = rhs.mEnterStateFunctions;
	mLeaveStateFunctions = rhs.mLeaveStateFunctions;
	mEventIdsToOtherStates = rhs.mEventIdsToOtherStates;
}

CybertronCoreAPI FiniteStateMachine& FiniteStateMachine::operator = (const FiniteStateMachine& rhs)
{
	FiniteStateMachine temp(rhs);
	swap(temp);

	return *this;
}

CybertronCoreAPI FiniteStateMachine::~FiniteStateMachine()
{
	clear();
}

void FiniteStateMachine::zeroMembers()
{
	mCurrentState = 0;
	mEnterStateFunctions.clear();
	mLeaveStateFunctions.clear();
	mEventIdsToOtherStates.clear();

	mEnterStateFunctions.resize(1);
	mLeaveStateFunctions.resize(1);
	mEventIdsToOtherStates.resize(1);
	mEventIdsToOtherStates[0].resize(1);
}

CybertronCoreAPI void FiniteStateMachine::swap(FiniteStateMachine& rhs)
{
	std::swap(mCurrentState, rhs.mCurrentState);
	mEnterStateFunctions.swap(rhs.mEnterStateFunctions);
	mLeaveStateFunctions.swap(rhs.mLeaveStateFunctions);
	mEventIdsToOtherStates.swap(rhs.mEventIdsToOtherStates);
}

CybertronCoreAPI void FiniteStateMachine::clear()
{
	zeroMembers();
}

CybertronCoreAPI int FiniteStateMachine::getCurrentState() const
{
	return mCurrentState;
}

CybertronCoreAPI void FiniteStateMachine::onEvent(int eventId)
{
	if (eventId <= 0)
	{
		return;
	}

	for (size_t i = 0; i < mEventIdsToOtherStates[mCurrentState].size(); ++i)
	{
		if (mEventIdsToOtherStates[mCurrentState][i] == eventId)
		{
			if (mLeaveStateFunctions[mCurrentState] != nullptr)
			{
				(mLeaveStateFunctions[mCurrentState])();
			}
			mCurrentState = (int)i;
			if (mEnterStateFunctions[mCurrentState] != nullptr)
			{
				(mEnterStateFunctions[mCurrentState])();
			}
			return;
		}
	}
}

CybertronCoreAPI void FiniteStateMachine::addState(
	int state,
	std::function<void()> funcOnEnter,
	std::function<void()> funcOnLeave)
{
	if (state < 0)
	{
		return;
	}

	if (state >= (int)(mEventIdsToOtherStates.size()))
	{
		mEventIdsToOtherStates.resize((size_t)state + 1);
		mEnterStateFunctions.resize((size_t)state + 1);
		mLeaveStateFunctions.resize((size_t)state + 1);

		for (size_t i = 0; i < mEventIdsToOtherStates.size(); ++i)
		{
			mEventIdsToOtherStates[i].resize((size_t)state + 1);
		}

		mEnterStateFunctions[state] = funcOnEnter;
		mLeaveStateFunctions[state] = funcOnLeave;
	}
}

CybertronCoreAPI void FiniteStateMachine::createTransition(
	int fromState,
	int toState,
	int eventId)
{
	if (fromState < 0 || fromState >= (int)mEventIdsToOtherStates.size())
	{
		return;
	}
	if (toState < 0 || toState >= (int)mEventIdsToOtherStates.size())
	{
		return;
	}

	for (size_t i = 0; i < mEventIdsToOtherStates[fromState].size(); ++i)
	{
		if (i == (size_t)toState)
		{
			continue;
		}
		if (mEventIdsToOtherStates[fromState][i] == eventId)
		{
			throw std::runtime_error("Duplicated Transition!");
			return;
		}
	}

	mEventIdsToOtherStates[fromState][toState] = eventId;
}

CYBERTRON_END
